package star.crust.cartspace.framework;

import java.util.ArrayList;
import java.util.Collections;

import star.core.util.geometry.DoubleXYZ;
import star.core.util.geometry.POTransform;

public class CrtMeshPrimitive {
	protected String type = ""; // "triangle", "trifan"
	protected CrtMeshVertex vertices[] = null;
	protected int size = 0;
	private ArrayList<CrtMeshVertex> vertici;
	private static int MAXSIZE = 32;
	private CrtAabbPlane plneq;
	public ArrayList<DoubleXYZ> seg1 = new ArrayList<DoubleXYZ>();
	public ArrayList<DoubleXYZ> seg2 = new ArrayList<DoubleXYZ>();

	public CrtMeshPrimitive(String type, int dimension) {
		this.type = type;
		vertices = new CrtMeshVertex[dimension];
	}
	
	/**
	 * Costruttori usati da Solid per la creazione dei poligoni.
	 * @param vertici
	 */
	public CrtMeshPrimitive(ArrayList<CrtMeshVertex> vertici) {
		this.vertici= new ArrayList<CrtMeshVertex>();
		this.vertici.addAll(vertici);
		this.plneq= new CrtAabbPlane();
		double[] normal = new double[3];
		DoubleXYZ refpt= new DoubleXYZ(0, 0, 0);
		for (int i = 0, j = this.vertici.size()-1; i < this.vertici.size(); j = i++) {
			normal[0] += (this.vertici.get(j).dblposition().getY() - this.vertici.get(i).dblposition().getY()) * (this.vertici.get(j).dblposition().getZ() + this.vertici.get(i).dblposition().getZ());
			normal[1] += (this.vertici.get(j).dblposition().getZ() - this.vertici.get(i).dblposition().getZ()) * (this.vertici.get(j).dblposition().getX() + this.vertici.get(i).dblposition().getX());
			normal[2] += (this.vertici.get(j).dblposition().getX() - this.vertici.get(i).dblposition().getX()) * (this.vertici.get(j).dblposition().getY() + this.vertici.get(i).dblposition().getY());

			refpt = refpt.setSum(new DoubleXYZ(this.vertici.get(i).dblposition().getX(), this.vertici.get(i).dblposition().getY(), this.vertici.get(i).dblposition().getZ()));
		}
		refpt.setDivide(this.vertici.size());
		plneq.setValue(normal, refpt);
	}
	
	public CrtMeshPrimitive(CrtMeshPrimitive p, POTransform t)
	{
		this.vertici= new ArrayList<CrtMeshVertex>();
		for (int i = 0; i < p.vertici.size(); i++) this.vertici.add(new CrtMeshVertex(p.vertici.get(i).dblposition().getX(), p.vertici.get(i).dblposition().getY(), p.vertici.get(i).dblposition().getZ())); 
		this.plneq=new CrtAabbPlane(p.plneq,t);
		this.transform(this.vertici,t);
	}


	public void addVertex(CrtMeshVertex vertex) {
		if (size == vertices.length)
			return;
		vertices[size] = vertex;
		size++;
	}

	public String getType() {
		return type;
	}

	public int size() {
		if (this.vertices != null) return size;
		else return this.vertici.size();
	}

	public String toString() {
		if (this.vertices != null) {

			String doc = "    vertices : ";
			for (int i = 0; i < vertices.length; i++)
				doc += vertices[i].toString() + " ";
			return doc;
		}
		else {
			return "nverts: "+this.vertici.size()+"\nverts: " +
			this.vertici.get(0).dblposition().getX() + "\t" + this.vertici.get(0).dblposition().getY() + "\t" + this.vertici.get(0).dblposition().getZ() +"\t" + 
			this.vertici.get(1).dblposition().getX() + "\t" + this.vertici.get(1).dblposition().getY() + "\t" + this.vertici.get(1).dblposition().getZ() +"\t" + 
			this.vertici.get(2).dblposition().getX() + "\t" + this.vertici.get(2).dblposition().getY() + "\t" + this.vertici.get(2).dblposition().getZ()+"\t" +
			this.vertici.get(3).dblposition().getX() + "\t" + this.vertici.get(3).dblposition().getY() + "\t" + this.vertici.get(3).dblposition().getZ() + 
			"\nplane.norm " + plneq.toString() + "\tplane.dist " + plneq.getDist();
		}
	}

	public CrtMeshVertex getVertex(int i) {
		if (this.vertices != null) {
			if (i < 0 || i >= size)
				return null;
			return vertices[i];

		} else {
			return vertici.get(i);	
		}
	}


	private void transform(ArrayList<CrtMeshVertex> v, POTransform t)
	{
		for(int i=0; i<v.size(); i++)
		{
			v.get(i).setPosition(this.operator(v.get(i).dblposition(),t));
		}
	}

	private DoubleXYZ operator(DoubleXYZ p,POTransform t)
	{
		return new DoubleXYZ(t.getPosition().getX()+t.rowdot(0, p), t.getPosition().getY()+t.rowdot(1, p), t.getPosition().getZ()+t.rowdot(2, p));
	}

	/**
	 * Metodo di verifica della collisioni tra poligoni
	 * @param poly1
	 * @param poly2
	 * @return
	 */
	public static boolean intersect(CrtMeshPrimitive poly1,CrtMeshPrimitive poly2)
	{
		double v[] =CrtAabbUtil.cross(poly1.getPlane().getNorm(), poly2.getPlane().getNorm());
		if(!CrtAabbUtil.approxZero(v))
		{
			ArrayList<DoubleXYZ> seg1=cut(poly1,poly2.getPlane());
			if(seg1.size()!=0)
			{
				ArrayList<DoubleXYZ> seg2=cut(poly2,poly1.getPlane());
				if (seg2.size()!=0) {
					int x=CrtAabbUtil.closestAxis(v);
					setPointAxis(seg1,x);
					setPointAxis(seg2,x);
					Collections.sort(seg1);
					Collections.sort(seg2);
					int i = 0; 
					int j = 0;
					while (i < seg1.size() && j < seg2.size()) 
					{
						if (seg1.get(i+1).getElement(x) < seg2.get(j).getElement(x)) i += 2;
						else if ( seg2.get(j+1).getElement(x) < seg1.get(i).getElement(x)) j += 2;
						else {
							return true;  
						}
					}
				}
			}
		}
		return false;  

	}

	private static void setPointAxis(ArrayList<DoubleXYZ> p, int x)
	{
		for(int i=0; i<p.size(); i++)
		{
			p.get(i).setAxis(x);
		}
	}

	private static ArrayList<DoubleXYZ> cut(CrtMeshPrimitive p,CrtAabbPlane plane)
	{
		final double[] dist= new double[MAXSIZE];
		ArrayList<DoubleXYZ> temp= new ArrayList<DoubleXYZ>();
		temp.clear();
		transformCut(p.getVertices(), plane, dist);
		int prev=p.size()-1;
		double t = 0;
		for (int i = 0; i < p.size(); ++i) 
		{
			if ((dist[i] >= 0) != (dist[prev] >= 0)) 
			{
				t = dist[i] / (dist[i] - dist[prev]);
				DoubleXYZ tempp=p.getVertex(prev).dblposition().setMinusPoint(p.getVertex(i).dblposition());
				tempp = tempp.setProduct(t);
				tempp = tempp.setSum(p.getVertex(i).dblposition());
				temp.add(tempp);
			}
			prev=i;
		}
		return temp;

	}

	private static void transformCut(ArrayList<CrtMeshVertex> v, CrtAabbPlane plane, double[] dist)
	{
		for(int i=0; i<v.size(); i++) dist[i]=operatorPlane(v.get(i).dblposition(),plane);
	}

	private static double operatorPlane(DoubleXYZ p, CrtAabbPlane plane)
	{
		return p.dot(plane.getNorm()) - plane.getDist();
	}



	public void setVertex(ArrayList<CrtMeshVertex> verts)
	{
		this.vertici.clear();
		this.vertici.addAll(verts);
	}

	public void setPlane(CrtAabbPlane pln)
	{
		this.plneq=pln;
	}

	public ArrayList<CrtMeshVertex> getVertices()
	{
		return this.vertici;
	}

	public CrtAabbPlane getPlane()
	{
		return this.plneq;
	}

	public double[] interval(CrtAabbPlane plane, double b, double e) {

		b = e = plane.operator(this.vertici.get(0).dblposition());
		for (int i = 1; i < this.vertici.size(); ++i) {
			double d = plane.operator(this.vertici.get(i).dblposition());		 
			b=CrtAabbUtil.set_min(b, d);
			e=CrtAabbUtil.set_max(e, d);
		}
		double[] interv = new double[2];
		interv[0]=b;
		interv[1]=e;
		return interv;
	}

	public void swap(CrtMeshPrimitive cswap) {
		ArrayList<CrtMeshVertex> vertnull= new ArrayList<CrtMeshVertex>();
		vertnull.clear();
		CrtMeshPrimitive temp= new CrtMeshPrimitive(vertnull);
		temp.setVertex(this.vertici);
		temp.setPlane(this.plneq);
		this.setVertex(cswap.getVertices());
		this.setPlane(cswap.getPlane());
		cswap.setVertex(temp.getVertices());
		cswap.setPlane(temp.getPlane());
		temp=null;
	}
}
